﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using TNCGameEngine.Content;
using TNCGameEngine.Graphics;

namespace TNCGameEngine.Input
{
    public abstract class Cursor
    {

        #region "Properties"

        private Vector2 velocity = Vector2.Zero;
        /// <summary>
        /// Velocity on the cursor movement; associated with GamePad.
        /// </summary>
        /// <remarks>Gets or sets the velocity.</remarks>
        public Vector2 Velocity
        {
            get { return this.velocity; }
            set { this.velocity = value; }
        }

        private Sprite sprite = null;
        /// <summary>
        /// The cursor icon to be displayed when drawn on screen.
        /// </summary>
        /// <remarks>Gets or sets the image.</remarks>
        public Sprite Sprite
        {
            get { return this.sprite; }
            set { this.sprite = value; }
        }

        private Vector2 location = Vector2.Zero;
        /// <summary>
        /// Location of the cursor.
        /// </summary>
        /// <remarks>Gets or sets the location. Setting enabled to force the cursor if desired.</remarks>
        public Vector2 Location
        {
            get { return this.location; }
            set { this.location = value; }
        }

        private bool useMouse = true;
        /// <summary>
        /// Indicates whether the mouse movements are to be followed.
        /// </summary>
        /// <remarks>Gets or sets the boolean value to indicate whether movements are to be used.</remarks>
        public bool UseMouse
        {
            get { return this.useMouse; }
            set { this.useMouse = value; }
        }

        private Vector2 hotspot = Vector2.Zero;
        /// <summary>
        /// Hotsopt of the cursor.
        /// </summary>
        /// <remarks>The active location of the cursor relative to the top-left corner of the image.</remarks>
        /// <value>Vector2.Zero</value>
        public Vector2 HotSpot
        {
            get { return this.hotspot; }
            set { this.hotspot = value; }
        }

        private bool centerHotspot = false;
        /// <summary>
        /// Indicates whether the hotspot is the center of the image.
        /// </summary>
        /// <remarks>If set to true, it will override the actual hotspot value when drawing the image.</remarks>
        /// <value>false</value>
        public bool CenterHotSpot
        {
            get { return this.centerHotspot; }
            set { this.centerHotspot = value; }
        }

        #endregion

        #region "Abstract Methods"

        /// <summary>
        /// Load any contents such as the sprite (cursor icon), etc.
        /// </summary>
        /// <param name="loader">Content manager, can be shared or not.</param>
        public abstract void LoadContent(ContentLoader loader);

        /// <summary>
        /// Unload any contents such as the sprite (cursor icon), etc.
        /// </summary>
        /// <param name="content">Content manager, can be shared or not.</param>
        public abstract void UnloadContent(SharedContentManager content);

        #endregion

        #region "Methods"

        /// <summary>
        /// Used to initialize the derived class. Must be called within the Constructor.
        /// </summary>
        public void PreInitialize()
        {


        }

        /// <summary>
        /// Calculates a ray that's generated from the current point of the cursor.
        /// </summary>
        /// <param name="g">Graphics device that holds viewport information.</param>
        /// <param name="projection">The projection matrix.</param>
        /// <param name="view">The view matrix.</param>
        /// <returns></returns>
        public Ray CalculateCursorRay(GraphicsDevice g, Matrix projection, Matrix view)
        {
           
            Vector3 nearSource = new Vector3(this.location, 0f);
            Vector3 farSource = new Vector3(this.location, 1f);
            
            Vector3 nearPoint = g.Viewport.Unproject(nearSource, projection, view, Matrix.Identity);
            Vector3 farPoint = g.Viewport.Unproject(farSource, projection, view, Matrix.Identity);

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            return new Ray(nearPoint, direction);

        }

        /// <summary>
        /// Indicates whether the cursor is currently within the viewport of the specified graphics device.
        /// </summary>
        /// <param name="g">The graphics device that has the questionable viewport.</param>
        /// <returns>True if the cursor is within the graphics device, otherwise false.</returns>
        public bool IsInsideViewport(GraphicsDevice g)
        {

            Viewport v = g.Viewport;
            return (this.location.X >= v.Bounds.Left
                    && this.location.X <= v.Bounds.Right
                    && this.location.Y >= v.Bounds.Top
                    && this.location.Y <= v.Bounds.Bottom);

        }

        /// <summary>
        /// The general update function. Gamepad cursor movement
        /// </summary>
        /// <param name="gameTime">The gametime information.</param>
        public virtual void Update(GameTime gameTime)
        {

            MouseState mouseState = Mouse.GetState();

            if (this.useMouse)
            {
                this.Location = new Vector2(mouseState.X, mouseState.Y);
            }

        }

        /// <summary>
        /// Draws the sprite (mouse icon) if already set.
        /// </summary>
        /// <param name="spritebatch">Sprite batch associated with the current frame.</param>
        public virtual void Draw(SpriteBatch spritebatch)
        {
            if (this.sprite != null)
            {
                if (this.centerHotspot)
                {
                    sprite.DrawCentered(spritebatch, this.location);
                }
                else
                {
                    sprite.Draw(spritebatch, this.location - this.hotspot);
                }
            }
        }

        #endregion

    }
}
